package com.meme.io.editor;

import org.apache.log4j.Logger;
import org.w3c.dom.Element;
import org.w3c.dom.Document;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;

import com.meme.editor.EntityObject;
import com.meme.editor.model.sprite2d.*;

import com.meme.util.FileUtil;
import com.meme.util.XmlUtil;

import java.util.List;
import java.util.LinkedList;


public class Sprite2dXmlFileLoader 
{
	static Logger logger = Logger.getLogger (Sprite2dXmlFileLoader.class);
	
	String mSprite2dXmlFilename;

	
	public Sprite2dFile loadFromXmlDocument (Document document, String filename)
	{
		mSprite2dXmlFilename = filename;
		
		Sprite2dFile sprite2dFile = new Sprite2dFile ();
		
		loadSprite2dFile (document, sprite2dFile);
		
		evalEntityRefs (sprite2dFile);
		
		sprite2dFile.setHostFilename (filename);
		
    	sprite2dFile.commitModifications();


		return sprite2dFile;
	}
	
	
//---------------------------------------------------------------------------	
// 
//---------------------------------------------------------------------------	
	


	void loadSprite2dFile (Document document, Sprite2dFile sprite2dFile)
	{
		Element sprite2dElement = XmlUtil.getChildElementByName (document, "Sprite2d");
		
		String version = sprite2dElement.getAttribute("version");
		
		logger.assertLog(version.equals("1.0"), "version is not 1.0.");
		
		
		// module image set
		{	
			Element element = XmlUtil.getChildElementByName(sprite2dElement, "ModuleImageSet");				

			Sprite2dFile.ModuleImageSet moduleImageSet = sprite2dFile.getModuleImageSet();
			moduleImageSet.setName ( element.getAttribute("name") );
			
			Element[] children = XmlUtil.getChildElementsByName (element, "ModuleImage");
			for (Element childElement : children)
			{
				readModuleImage (sprite2dFile, moduleImageSet, childElement);
			}
		}
		
		// pure module group set
		{
			Element element = XmlUtil.getChildElementByName(sprite2dElement, "PureModuleGroupSet");				

			Sprite2dFile.PureModuleGroupSet pureModuleGroupSet = sprite2dFile.getPureModuleGroupSet();
			pureModuleGroupSet.setName ( element.getAttribute("name") );
			
			Element[] children = XmlUtil.getChildElementsByName (element, "PureModuleGroup");
			for (Element childElement : children)
			{
				readPureModuleGroup (sprite2dFile, pureModuleGroupSet, childElement);
			}
		}
		
		// composited module group set
		{
			Element element = XmlUtil.getChildElementByName(sprite2dElement, "CompositedModuleGroupSet");				

			Sprite2dFile.CompositedModuleGroupSet compositedModuleGroupSet = sprite2dFile.getCompositedModuleGroupSet();
			compositedModuleGroupSet.setName ( element.getAttribute("name") );
			
			Element[] children = XmlUtil.getChildElementsByName (element, "CompositedModuleGroup");
			for (Element childElement : children)
			{
				readCompositedModuleGroup (sprite2dFile, compositedModuleGroupSet, childElement);
			}
		}
		
		// animated module group set
		{		
			Element element = XmlUtil.getChildElementByName(sprite2dElement, "AnimatedModuleGroupSet");				

			Sprite2dFile.AnimatedModuleGroupSet animatedModuleGroupSet = sprite2dFile.getAnimatedModuleGroupSet();
			animatedModuleGroupSet.setName ( element.getAttribute("name") );
			
			Element[] children = XmlUtil.getChildElementsByName (element, "AnimatedModuleGroup");
			for (Element childElement : children)
			{
				readAnimatedModuleGroup (sprite2dFile, animatedModuleGroupSet, childElement);
			}
		}

		
		// predefined model set
		{		
			Element element = XmlUtil.getChildElementByName(sprite2dElement, "PredeinedModelSet");				

			Sprite2dFile.ModelSet predefinedModelSet = sprite2dFile.getPredefinedModelSet();
			
			predefinedModelSet.setName ( element.getAttribute("name") );
			
			Element[] children = XmlUtil.getChildElementsByName (element, "ModelSet");
			for (Element childElement : children)
			{
				readModelSet (sprite2dFile, predefinedModelSet, childElement);
			}
		}

		// tile palette
		{
			Element element = XmlUtil.getChildElementByName(sprite2dElement, "TilePaletteSet");
			Sprite2dFile.TilePaletteSet tilePaletteSet = sprite2dFile.getTilePaletteSet();
			
			tilePaletteSet.setName (element.getAttribute("name"));
			
			Element[] children = XmlUtil.getChildElementsByName (element, "TilePalette");
			for (Element childElement : children)
			{
				readTilePalette (sprite2dFile, tilePaletteSet, childElement);
			}
		}
		
		// tile background
		{
			Element element = XmlUtil.getChildElementByName(sprite2dElement, "Tiled2dBackgroundSet");
			Sprite2dFile.TiledBackgroundSet tiledBackgroundSet = sprite2dFile.getTiledBackgroundSet();
			
			tiledBackgroundSet.setName (element.getAttribute("name"));
			
			Element[] children = XmlUtil.getChildElementsByName (element, "Tiled2dBackground");
			for (Element childElement : children)
			{
				readTiled2dBackground (sprite2dFile, tiledBackgroundSet, childElement);
			}

		}
	}
	
	void readModuleImage (Sprite2dFile sprite2dFile, Sprite2dFile.ModuleImageSet moduleImageSet, Element element)
	{
		ModuleImage moduleImage = sprite2dFile.createNewModuleImage();
		
		String imageFilename = FileUtil.getFullPathFilename(
				element.getAttribute("image_url"), 
				mSprite2dXmlFilename );
		
		moduleImage.setName ( element.getAttribute("name") );
		moduleImage.setImageFilename ( imageFilename );
	}
	
	void readPureModuleGroup (Sprite2dFile sprite2dFile, Sprite2dFile.PureModuleGroupSet pureModuleGroupSet, Element element)
	{
		PureModuleGroup pureModuleGroup = sprite2dFile.createNewPureModuleGroup();
		
		pureModuleGroup.setName ( element.getAttribute("name") );
		pureModuleGroup.setModuleImage ( (ModuleImage) sprite2dFile.parseNodePath ( sprite2dFile.getModuleImageSet().getName() + "/" + element.getAttribute("image_ref") ) );
		
		Element[] children = XmlUtil.getChildElementsByName (element, "PureModule");
		for (Element childElement : children)
		{
			readPureModule (sprite2dFile, pureModuleGroup, childElement);
		}
	}
	
	void readPureModule (Sprite2dFile sprite2dFile, PureModuleGroup pureModuleGroup, Element element)
	{
		PureModule pureModule = pureModuleGroup.createNewModule();
		
		pureModule.setName ( element.getAttribute ("name") );
		pureModule.setX ( XmlUtil.parseAttributeAsInt(element, "x") );
		pureModule.setY ( XmlUtil.parseAttributeAsInt(element, "y") );
		pureModule.setWidth  ( XmlUtil.parseAttributeAsInt(element, "w") );
		pureModule.setHeight ( XmlUtil.parseAttributeAsInt(element, "h") );
	}
	
	void readCompositedModuleGroup (Sprite2dFile sprite2dFile, Sprite2dFile.CompositedModuleGroupSet compositedModuleGroupSet, Element element)
	{
		CompositedModuleGroup compositedModuleGroup = sprite2dFile.createNewCompositedModuleGroup();
		
		compositedModuleGroup.setName ( element.getAttribute ("name") );
		
		Element[] children = XmlUtil.getChildElementsByName (element, "CompositedModule");
		for (Element childElement : children)
		{
			readCompositedModule (sprite2dFile, compositedModuleGroup, childElement);
		}
	}
	
	void readCompositedModule (Sprite2dFile sprite2dFile, CompositedModuleGroup compositedModuleGroup, Element element)
	{
		CompositedModule compositedModule = compositedModuleGroup.createNewCompositedModule();
		
		compositedModule.setName ( element.getAttribute ("name") );
		
		Element[] children = XmlUtil.getChildElementsByName (element, "ModulePart");
		for (Element childElement : children)
		{
			readModulePart (sprite2dFile, compositedModule, childElement);
		}
	}
	
	void readModulePart (Sprite2dFile sprite2dFile, CompositedModule compositedModule, Element element)
	{
		ModulePart modulePart = compositedModule.createNewModulePart();
		
		modulePart.setName ( element.getAttribute ("name") );
		//modulePart.setModule ( (Module) sprite2dFile.parseNodePath ( element.getAttribute ("module_ref") ) );
		modulePart.setUserObject ( element.getAttribute ("module_ref") );
		modulePart.setOffsetX ( XmlUtil.parseAttributeAsInt(element, "offset_x") );
		modulePart.setOffsetY ( XmlUtil.parseAttributeAsInt(element, "offset_y") );
		modulePart.setFlipX ( XmlUtil.parseAttributeAsBoolean(element, "flip_x") );
		modulePart.setFlipY ( XmlUtil.parseAttributeAsBoolean(element, "flip_y") );
		modulePart.setPaletteID ( XmlUtil.parseAttributeAsInt(element, "palette") );
		modulePart.setFlags ( XmlUtil.parseAttributeAsInt(element, "flags") );
	}
	
	void readAnimatedModuleGroup (Sprite2dFile sprite2dFile, Sprite2dFile.AnimatedModuleGroupSet animatedModuleGroupSet, Element element)
	{
		AnimatedModuleGroup animatedModuleGroup = sprite2dFile.createNewAnimatedModuleGroup();
		
		animatedModuleGroup.setName ( element.getAttribute ("name") );
		
		Element[] children = XmlUtil.getChildElementsByName (element, "AnimatedModule");
		for (Element childElement : children)
		{
			readAnimatedModule (sprite2dFile, animatedModuleGroup, childElement);
		}
	}
	
	void readAnimatedModule (Sprite2dFile sprite2dFile, AnimatedModuleGroup animatedModuleGroup, Element element)
	{
		AnimatedModule animatedModule = animatedModuleGroup.createNewAnimatedModule();
		
		animatedModule.setName ( element.getAttribute ("name") );
		
		Element[] children = XmlUtil.getChildElementsByName (element, "ModuleSequence");
		for (Element childElement : children)
		{
			readModuleSequence (sprite2dFile, animatedModule, childElement);
		}
	}
	
	void readModuleSequence (Sprite2dFile sprite2dFile, AnimatedModule animatedModule, Element element)
	{
		ModuleSequence moduleSequence = animatedModule.createNewModuleSequence();
		
		moduleSequence.setName ( element.getAttribute ("name") );
		//moduleSequence.setModule ( (Module) sprite2dFile.parseNodePath ( element.getAttribute ("module_ref") ) );
		moduleSequence.setUserObject ( element.getAttribute ("module_ref") );
		moduleSequence.setOffsetX ( XmlUtil.parseAttributeAsInt(element, "offset_x") );
		moduleSequence.setOffsetY ( XmlUtil.parseAttributeAsInt(element, "offset_y") );
		moduleSequence.setFlipX ( XmlUtil.parseAttributeAsBoolean(element, "flip_x") );
		moduleSequence.setFlipY ( XmlUtil.parseAttributeAsBoolean(element, "flip_y") );
		moduleSequence.setPaletteID ( XmlUtil.parseAttributeAsInt(element, "palette") );
		moduleSequence.setFlags ( XmlUtil.parseAttributeAsInt(element, "flags") );
		moduleSequence.setDuration ( XmlUtil.parseAttributeAsInt(element, "duration") );
	}
	
	void readModelSet (Sprite2dFile sprite2dFile, Sprite2dFile.ModelSet predefinedModelSet, Element element)
	{
		
	}
	
	void evalEntityRefs (Sprite2dFile sprite2dFile)
	{
		List <EntityObject> moduleGroups = new LinkedList <EntityObject> ();
		
		moduleGroups.addAll ( sprite2dFile.getCompositedModuleGroupSet().getChildren() );
		moduleGroups.addAll ( sprite2dFile.getAnimatedModuleGroupSet().getChildren() );
		
		for (EntityObject entity : moduleGroups)
		{
			for (EntityObject moduleEntity : entity.getChildren ())
			{
				for (EntityObject partEntity : moduleEntity.getChildren())
				{
					ModulePart part = (ModulePart) partEntity;

					part.setModule ( (Module) sprite2dFile.parseNodePath ( (String) part.getUserObject() ) );
					
					part.setUserObject(null);
				}
			}
		}
	}
	
	void readTilePalette (Sprite2dFile sprite2dFile, Sprite2dFile.TilePaletteSet tilePaletteSet, Element element)
	{
		Tiled2dPalette palette = sprite2dFile.createNewTilePalette(); 
		
		int cell_width  = XmlUtil.parseAttributeAsInt(element, "cell_width");
		int cell_height = XmlUtil.parseAttributeAsInt(element, "cell_height");
		int cell_rows   = XmlUtil.parseAttributeAsInt(element, "cell_rows");
		int cell_cols   = XmlUtil.parseAttributeAsInt(element, "cell_cols");
		
		palette.setName ( element.getAttribute ("name") );
		palette.setCellSize (cell_width, cell_height);
		palette.setGridSize (cell_rows, cell_cols);
		
		int modules_count = cell_rows * cell_cols;
		Element[] children = XmlUtil.getChildElementsByName (element, "Tiled2dModule");
		logger.assertLog(children.length == cell_rows * cell_cols, "children.length != modules_count, !!!");		
		
		int index = 0;
		for (int row = 0; row < cell_rows; ++ row)
		{
			for (int col = 0; col < cell_cols; ++ col)
			{
				Tiled2dModule tile = palette.getTiled2dModuleByIndex (index);
				Element tileElement = children [index];
				
				logger.assertLog (tile.getRow() == row, "tile.getRow() != row, !!!");
				logger.assertLog (tile.getCol() == col, "tile.getCol() != col, !!!");
				logger.assertLog (tile.getName().equals(tileElement.getAttribute ("name")), "names are not matched !!!");
				
				tile.setModule ( (Module) sprite2dFile.parseNodePath ( tileElement.getAttribute ("module_ref") ) );
				tile.setPaletteID (XmlUtil.parseAttributeAsInt (tileElement, "palette") );
				tile.setFlipX (XmlUtil.parseAttributeAsBoolean (tileElement, "flip_x") );
				tile.setFlipY (XmlUtil.parseAttributeAsBoolean (tileElement, "flip_y") );
				
				++ index;
			}
		}
	}

	void readTiled2dBackground (Sprite2dFile sprite2dFile, Sprite2dFile.TiledBackgroundSet tiledBackgroundSet, Element element)
	{
		Tiled2dBackground tiled2dBackgrpund = sprite2dFile.createNewTiledBackground();
		
		int cell_width  = XmlUtil.parseAttributeAsInt(element, "cell_width");
		int cell_height = XmlUtil.parseAttributeAsInt(element, "cell_height");
		int cell_rows   = XmlUtil.parseAttributeAsInt(element, "cell_rows");
		int cell_cols   = XmlUtil.parseAttributeAsInt(element, "cell_cols");
		
		tiled2dBackgrpund.setName ( element.getAttribute ("name") );
		tiled2dBackgrpund.setCellSize (cell_width, cell_height);
		tiled2dBackgrpund.setGridSize (cell_rows, cell_cols);

		tiled2dBackgrpund.removeAllChildren(); // remove the default layer
		
		Element[] children = XmlUtil.getChildElementsByName (element, "Tiled2dLayer");
		for (Element childElement : children)
		{
			readTiled2dLayer (sprite2dFile, tiled2dBackgrpund, childElement);
		}
	}

	
	void readTiled2dLayer (Sprite2dFile sprite2dFile, Tiled2dBackground tiled2dBackgrpund, Element element)
	{
		Tiled2dLayer tiled2dLayer = tiled2dBackgrpund.createNewTiled2dLayer ();
		
		tiled2dLayer.setName ( element.getAttribute ("name") );
		
		String cellValuesString = element.getTextContent();
		if (cellValuesString != null)
		{
			String[] cellValueStrings = cellValuesString.split(",");
			
			logger.assertLog(cellValueStrings.length >= tiled2dLayer.getCellRows() * tiled2dLayer.getCellCols(), "some data lost !!!");
			
			int index = 0;
			for (int row = 0; row < tiled2dLayer.getCellRows(); ++ row)
			{
				for (int col = 0; col < tiled2dLayer.getCellCols(); ++ col)
				{
					Tiled2dLayerCell layerCell = tiled2dLayer.getLayerCellByPos(row, col);

					String valuesString = cellValueStrings [index ++];
					
					String [] valueStrings = valuesString.split (":");
					
					if (valueStrings[0].length() > 0)
					{
						String[] tile_module_path_ids = valueStrings[0].split ("-");
						int tilePaletteID = Integer.parseInt (tile_module_path_ids[0]);
						int tileRow = Integer.parseInt (tile_module_path_ids[1]);
						int tileCol = Integer.parseInt (tile_module_path_ids[2]);
						Tiled2dPalette palatte = (Tiled2dPalette) sprite2dFile.getTilePaletteSet().getChildByIndex(tilePaletteID);
						layerCell.setModule (palatte.getTiled2dModuleByPos (tileRow, tileCol));
					}
					
					// if (valueStrings[1].length() > 0)
					{
						int paletteID  = Integer.parseInt (valueStrings[1]);
						layerCell.setPaletteID (paletteID);
					}
					
					if (valueStrings.length > 2 && valueStrings[2].length() > 0)
					{
						layerCell.setFlipX (valueStrings[2].indexOf('x') >= 0);
						layerCell.setFlipY (valueStrings[2].indexOf('y') >= 0);
					}
				}
			}
		}
	}
}
